﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.ComponentModel;
using System.Xml;
using System.ComponentModel.Design;
using System.Reflection;
using System.Windows.Forms;


using UmcControls.DesignInterface;

using Wicresoft.Shell.Core;
using System.Text.RegularExpressions;

namespace UmcProvider.Shared
{
    public interface IUmc
    {
    }
    /// <summary>
    /// the ExcludePropertyAttribute which means the properties in the class should be excluded
    /// </summary>
    [AttributeUsage(AttributeTargets.Interface, Inherited = false, AllowMultiple = false), Serializable]
    public sealed class ExcludePropertyAttribute : Attribute
    { }

    /// <summary>
    /// This abstract class should be inherited by Umc control with defining the generation type
    /// at the same time , because this class implement the IConverter.
    /// The contorl in design view and contorl in code view is acutally different , .Net controls perform in design view ,
    /// otherwise umc controls in code view , this class implement this kind of converte.
    /// </summary>
    /// <typeparam name="C">should  be UmcControl type</typeparam>
    /// <typeparam name="T">should be .net control type</typeparam>
    public abstract class DefaultConverter<C, T> : IConverter
        where C : IUmc
        where T : IComponent
    {
        protected static T instance = Activator.CreateInstance<T>();
        protected static String[] excludedPropArr = { "Bounds", "Left", "Top", "ImeMode", "Parent", "WindowTarget" };
        //properties contain all the properties should be avaliable in the IDE
        protected static IEnumerable<String> properties = GetUmcProperties();




        public virtual XmlNode[] Serialize(IComponent root, XmlDocument document)
        {
            if (root is Control)
            {
                Control ctl = root as Control;

                XmlNode node = document.CreateNode(XmlNodeType.Element, ctl.GetType().Name, null);

                // common properties
                IUmcPropertyStore ips = (root as IUmcPropertyStore);
                foreach (var name in properties)
                {
                    object value = typeof(T).GetProperty(name).GetValue(root, null);
                    if (!EqualDefaultValue(name, value))
                    {
                        string p0property = ips.GetUmcProperty(name);
                        //if pre code does not define this property , the property will not stored , so we will get null here
                        if (p0property == null)
                        {
                            node.Attributes.Append(document.CreateAttribute(name))
                                .Value = GetUmcValue(value);
                        }
                        //otherwise we should use the pre code not the design defined
                        else
                        {
                            node.Attributes.Append(document.CreateAttribute(name))
                                .Value = p0property;
                            //after serialize remove it!
                            ips.RemoveUmcProperty(name);
                        }
                    }
                }
                //other stored properties
                foreach (KeyValuePair<string, string> k in ips.GetUmcPropertyList())
                {
                    node.Attributes.Append(document.CreateAttribute(k.Key))
                       .Value = k.Value;
                }

                return new XmlNode[] { node };
            }

            throw new NotImplementedException();
        }


        public virtual IComponent DeSerialize(IDesignerHost host, XmlDocument document, XmlNode[] node)
        {
            IComponent component = host.CreateComponent(typeof(T));
            //T component = Activator.CreateInstance<T>();

            if (component is Control)
            {
                foreach (XmlAttribute attr in node.First().Attributes)
                {
                    SetProperty(component, attr);
                }

                return component;
            }

            throw new NotImplementedException();
        }



        protected string AttributeReplace(string attr)
        {
            string ret = attr;
            string variablePattern = @"\$\w+";
            MatchCollection mc = Regex.Matches(ret, variablePattern);
            foreach (Match m in mc)
            {
                UmcVariable uvble;
                if (IntelliSenseManager.FullcontextVariable.TryGetValue(m.Value.Substring(1, m.Value.Length - 1), out uvble))
                    ret = ret.Replace(m.Value, uvble.VariableValue);
            }
            return ret;

        }

        private void SetProperty(IComponent component, XmlAttribute attr)
        {
            var prop = typeof(T).GetProperty(attr.Name);
            if (prop != null)
            {
                string attrValue = AttributeReplace(attr.Value);
                object value = GetUmcValue(attr.Name, attrValue, prop.PropertyType);
                if (value != null)
                {
                    prop.SetValue(component, value, null);
                }
                else
                {
                    //if the value is invalid we should use default value and store it for the serialize
                    (component as IUmcPropertyStore).AddUmcProperty(attr.Name, attr.Value);
                    prop.SetValue(component, GetUmcDefaultValue(attr.Name, attr.Value, prop.PropertyType), null);
                }
            }
            //since the property is not surpported we need  to store it
            else
            {
                (component as IUmcPropertyStore).AddUmcProperty(attr.Name, attr.Value);
            }
        }

        //since the expression can be Location="$x,$y",we need to provide a default value for some type , the child class
        //could override this method
        protected virtual object GetUmcDefaultValue(string name, string value, Type type)
        {
            if (type == typeof(String))
            {
                return string.Empty;
            }
            else if (type == typeof(Size))
            {
                return new Size(75, 23);
            }
            else if (type == typeof(Point))
            {
                return new Point(0, 0);
            }
            else if (type == typeof(Font))
            {
                //var source = value.Split(',');
                //return new Font(source[0], float.Parse(source[1]));
                return new Font("Microsoft Sans Serif", 8);
            }
            else if (type == typeof(Color))
            {
                //var source = value.Split(',');
                //return Color.FromArgb(int.Parse(source[0]), int.Parse(source[1]), int.Parse(source[2]));
                return Color.FromKnownColor(KnownColor.Control);
            }
            else if (type == typeof(Boolean))
            {
                //return bool.Parse(value);
                return true;
            }
            else if (type == typeof(Int32))
            {
                //return int.Parse(value);
                return 0;
            }
            else if (type == typeof(Char))
            {
                //return value[0];
                return ' ';
            }
            else if (type == typeof(ScrollBars))
            {
                //return Enum.Parse(typeof(ScrollBars), value);
                return ScrollBars.Both;
            }
            else if (type == typeof(View))
            {
                //return Enum.Parse(typeof(View), value);
                return View.List;
            }
            else if (type == typeof(Image))
            {
                return null;
            }
            else if (type == typeof(ImageList))
            {
                //TODO
                return null;
            }
            else
                return null;
        }


        private object GetUmcValue(string name, string value, Type type)
        {
            try
            {
                if (type == typeof(String))
                {
                    return value;
                }
                else if (type == typeof(Size))
                {
                    var source = value.Split(',');
                    return new Size(int.Parse(source[0]), int.Parse(source[1]));
                }
                else if (type == typeof(Point))
                {
                    var source = value.Split(',');
                    return new Point(int.Parse(source[0]), int.Parse(source[1]));
                }
                else if (type == typeof(Font))
                {
                    var source = value.Split(',');
                    return new Font(source[0], float.Parse(source[1]));
                }
                else if (type == typeof(Color))
                {
                    var source = value.Split(',');
                    return Color.FromArgb(int.Parse(source[0]), int.Parse(source[1]), int.Parse(source[2]));
                }
                else if (type == typeof(Boolean))
                {
                    return bool.Parse(value);
                }
                else if (type == typeof(Int32))
                {
                    return int.Parse(value);
                }
                else if (type == typeof(Char))
                {
                    return value[0];
                }
                else if (type == typeof(ScrollBars))
                {
                    return Enum.Parse(typeof(ScrollBars), value);
                }
                else if (type == typeof(View))
                {
                    return Enum.Parse(typeof(View), value);
                }
                else if(type == typeof(Image))
                {
                    return null;
                }
                else if (type == typeof(ImageList))
                {
                    //TODO
                    return null;
                }
                else
                    return null;
            }
            catch
            {
                return null;
            }
        }



        #region properties filter

        private static IEnumerable<String> GetIncludeUmcProperties()
        {
            List<String> props = new List<String>();

            typeof(C).GetInterfaces().All(p =>
            {
                props.AddRange(p.GetProperties().Select(q => q.Name));
                return true;
            });
            return props;
        }

        private static IEnumerable<String> GetExceptUmcProperties()
        {
            List<Type> allTypes = new List<Type>();
            List<Type> allInterfaces = new List<Type>();
            List<String> allProps = new List<String>();
            List<String> excludeProps = new List<String>();
            BindingFlags propFilter = BindingFlags.Public | BindingFlags.Instance;

            for (Type curType = typeof(T); null != curType; curType = curType.BaseType)
            {
                allTypes.Add(curType);
                allInterfaces.AddRange(curType.GetInterfaces());
            }

            // Get all these properties
            foreach (Type t in allTypes)
            {
                allProps.AddRange(
                    t.GetProperties(propFilter).
                      Where(p => (p.CanRead && p.CanWrite && p.IsDefined(typeof(CategoryAttribute), false))).
                      Select(p => p.Name));
            }

            excludeProps.AddRange(excludedPropArr);
            // Get all these excluded properties
            foreach (Type t in allInterfaces.Where(i => i.IsDefined(typeof(ExcludePropertyAttribute), false)).Distinct())
                excludeProps.AddRange(t.GetProperties().Select(p => p.Name));

            return allProps.Distinct().Except(excludeProps.Distinct()).ToArray();
        }

        // Helper
        /// <summary>
        /// since each umc control should inherit from DefaultConverter and one interface , the interface should be decrete by
        /// either ExcludePropertyAttribute or none.
        /// </summary>
        /// <returns></returns>
        private static IEnumerable<String> GetUmcProperties()
        {
            Type firstExcludeInterface = null;
            IEnumerable<String> props = null;

            //see if the umc control is decreted by ExcludePropertyAttribute
            firstExcludeInterface = typeof(C).GetInterfaces().
                FirstOrDefault(t => t.IsDefined(typeof(ExcludePropertyAttribute), false));

            if (null != firstExcludeInterface)
            {
                props = GetExceptUmcProperties();
            }
            else
            {
                props = GetIncludeUmcProperties();
            }

            return props.ToArray();
        }


        #endregion



        private bool EqualDefaultValue(string name, object value)
        {
            object defaultValue = typeof(T).GetProperty(name).GetValue(instance, null);

            if (defaultValue == null && value == null)
                return true;
            else if (defaultValue == null)
                return false;
            else if (value == null)
                return false;
            else if (defaultValue.ToString() == value.ToString())
                return true;
            else
                return false;
        }

        private string GetUmcValue(object v)
        {
            //TODO:

            if (v is Size)
                return string.Format("{0},{1}", ((Size)v).Width, ((Size)v).Height);
            else if (v is Point)
                return string.Format("{0},{1}", ((Point)v).X, ((Point)v).Y);
            else if (v is Font)
                return string.Format("{0}, {1}", ((Font)v).Name, ((Font)v).Size);
            else if (v is Color)
                return string.Format("{0},{1},{2}", ((Color)v).R, ((Color)v).G, ((Color)v).B);
            else if (v is ImageList)
                return string.Format("Local:{0}", ((ImageList)v).Tag);
            else
                return v.ToString();
        }
    }

    internal static class UmcPropertyConvert
    {
        public static Size ToSize(string value)
        {
            try
            {
                var source = value.Split(',');
                return new Size(int.Parse(source[0]), int.Parse(source[1]));
            }
            catch
            {
                return new Size(75, 23);
            }
        }
        public static Point ToPoint(string value)
        {
            try
            {
                var source = value.Split(',');
                return new Point(int.Parse(source[0]), int.Parse(source[1]));
            }
            catch
            {
                return new Point(0, 0);
            }
        }
        public static Font ToFont(string value)
        {
            try
            {
                var source = value.Split(',');
                return new Font(source[0], float.Parse(source[1])); 
            }
            catch
            {
                return new Font("Microsoft Sans Serif", 8);
            }
        }
        public static Color ToColor(string value)
        {
            try
            {
                var source = value.Split(',');
                return Color.FromArgb(int.Parse(source[0]), int.Parse(source[1]), int.Parse(source[2]));
            }
            catch 
            {
                return Color.FromKnownColor(KnownColor.Control);
            }
        }
        public static Boolean ToBoolean(string value)
        {
            try
            {
                return bool.Parse(value);
            }
            catch
            {
                return false;
            }
        }
        public static object ToScrollBars(string value)
        {
            try
            {
                return Enum.Parse(typeof(ScrollBars), value);
            }
            catch
            {
                return ScrollBars.None;
            }
        }


        public static string ToString(Size s)
        {
            return string.Format("{0},{1}", s.Width, s.Height);
        }
        public static string ToString(Point p)
        {
            return string.Format("{0},{1}", p.X, p.Y);
        }
        public static string ToString(Font f)
        {
            return string.Format("{0},{1}", f.Name, f.Size);
        }
        public static string ToString(Color c)
        {
            return string.Format("{0},{1},{2}", c.R, c.G, c.B);
        }
        public static string ToString(Boolean b)
        {
            return b.ToString();
        }
        public static string ToString(ScrollBars sbs)
        {
            return sbs.ToString();
        }

    }
}

